﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using Asol.Reporting.Editor.Editor;
using Asol.Reporting.Editor.Data;
using Asol.Reporting.Editor.Components;
using Asol.Reporting.Editor.ComponentsLayer;
using Asol.Reporting.Support;
using Asol.Reporting.Support.Components;

namespace Asol.Reporting.Editor.ReportObjects
{
    #region class ReportGraphBar : komponenta reportu, která zobrazuje graf typu Bar
    /// <summary>
    /// Třída zobrazující graf 3D sloupců
    /// </summary>
    public class ReportGraphBar : ReportGraph3D
    {
        #region Konstrukce
        public ReportGraphBar()
        {
            this.Data = new GraphDataBar(this);
            this.EditorData = new GraphDataBar(this);
            this.EditorData.FillValuesForEditor();
        }
        #endregion
        #region Vlastnosti v Menu + Toolbaru (UI)
        /// <summary>
        /// Property, která obsahuje veškeré informace pro tvorbu UI
        /// </summary>
        protected override MenuItemInfo MenuInfo
        {
            get
            {
                return new MenuItemInfo()
                {
                    Availability = MenuItemAvailability.Users,
                    GroupName = "20",
                    OrderInGroup = 11,
                    Name = "Graf Bar3D",
                    TooltipText = "Vloží graf typu Bar3D",
                    ImageSmall = Asol.Reporting.Editor.Pics.IconLibrary22.InsertChartBar_FromFile,
                    ImageMedium = null
                };
            }
        }
        #endregion
        #region Vykreslení v editoru, SizeChange
        /// <summary>
        /// Data grafu pro tisk reportu
        /// </summary>
        [Browsable(false)]
        public GraphDataBar EditorData { get; private set; }
        /// <summary>
        /// Vytvoří a vrátí sadu střípků, které reprezentují graf. Pro editaci (na základě dat v this.EditorData).
        /// </summary>
        /// <param name="currentVisualBound"></param>
        /// <returns></returns>
        protected override IEnumerable<GraphDrawPiece> EditorGetGraphPieces(RectangleF currentVisualBound)
        {
            return this.EditorData.GetGraphPieces(currentVisualBound);
        }
        /// <summary>
        /// Voláno v době editace.
        /// Umožní prvku, aby korigoval souřadnice a/nebo velikost prvku.
        /// </summary>
        /// <param name="args"></param>
        protected override void EditorLogicalBoundsChange(ReportObjectLogicalBoundsChangeArgs args)
        {
            switch (args.ChangeType)
            {
                case LogicalBoundsChangeType.Insert:
                    // Vkládání nového objektu: bod počátku lze ignorovat, zde nastavíme optimální úvodní logickou velikost (v milimetrech):
                    args.LogicalBounds = new RectangleF(0, 0, 60f, 40f);
                    break;
            }
        }
        #endregion
        #region Ostrá data grafu pro tisk
        /// <summary>
        /// Data grafu pro tisk reportu
        /// </summary>
        [Browsable(false)]
        public GraphDataBar Data { get; private set; }
        #endregion
    }
    #endregion
    #region class GraphDataBar : datová třída, jejímž účelem je vypočítat data grafu Bar a vypočtená data převést na grafické prvky metodou GetGraphPieces()
    /// <summary>
    /// GraphDataBar : datová třída, jejímž účelem je vypočítat data grafu Bar a vypočtená data převést na grafické prvky metodou GetGraphPieces()
    /// </summary>
    public class GraphDataBar : GraphData<GraphDataItemBarInfo>
    {
        public GraphDataBar(ReportGraphBar owner)
        {
            this.Owner = owner;
        }
        /// <summary>
        /// Vlastník grafu, obsahuje parametry pro zobrazení grafu
        /// </summary>
        protected ReportGraphBar Owner { get; private set; }
        /// <summary>
        /// Aktuální dimenze grafu
        /// </summary>
        protected Block3D CurrentBlock { get; private set; }
        #region Tvorba dat pro zobrazení v editoru
        /// <summary>
        /// Naplní do this data pro zobrazení v editoru
        /// </summary>
        public override void FillValuesForEditor()
        {
            this.GraphItemList = new List<GraphDataItem<GraphDataItemBarInfo>>();
            this.GraphItemList.Add(new GraphDataItem<GraphDataItemBarInfo>() { Position = 4, Series = 0, Value = 100m, Caption = "Ddd", GraphColor = Color.Wheat });
            this.GraphItemList.Add(new GraphDataItem<GraphDataItemBarInfo>() { Position = 1, Series = 0, Value = 250m, Caption = "Aaa", GraphColor = Color.LightCyan });
            this.GraphItemList.Add(new GraphDataItem<GraphDataItemBarInfo>() { Position = 2, Series = 0, Value = 700m, Caption = "Bbb", GraphColor = Color.Linen });
            this.GraphItemList.Add(new GraphDataItem<GraphDataItemBarInfo>() { Position = 3, Series = 0, Value = 1400m, Caption = "Ccc", GraphColor = Color.MistyRose });
            this.GraphItemList.Add(new GraphDataItem<GraphDataItemBarInfo>() { Position = 5, Series = 0, Value = 1800m, Caption = "Eee", GraphColor = Color.Gainsboro });
        }
        #endregion
        #region Generování grafických prvků
        /// <summary>
        /// Klíčová metoda každého grafu, kdy transformuje svoje logická data do vizuálních střípků, které tvoří aktuální reprezentaci grafu.
        /// Zobrazování provádí 
        /// </summary>
        public override IEnumerable<GraphDrawPiece> GetGraphPieces(RectangleF bounds)
        {
            List<GraphDrawPiece> result = new List<GraphDrawPiece>();
            View3D view3D = this.Owner.CreateView3D(bounds);
            this.AddGraphPiecesBound(bounds, view3D, result);
            this.AddGraphPiecesDataItems(bounds, view3D, result);
            return result;
        }
        protected void AddGraphPiecesBound(RectangleF bounds, View3D view3D, List<GraphDrawPiece> result)
        {
            this.CurrentBlock = this.Owner.CreateGraphBlock3D(bounds);
            this.AddGraphPiecesBack(bounds, this.CurrentBlock, view3D, this.Owner.BackColor, result);
        }
        protected void AddGraphPiecesDataItems(RectangleF bounds, View3D view3D, List<GraphDrawPiece> result)
        {
            List<GraphDataItem<GraphDataItemBarInfo>> items = this.PrepareDataItems(view3D);

            foreach (GraphDataItem<GraphDataItemBarInfo> item in items)
                this.AddGraphPiecesDataItemOne(bounds, view3D, item, result);
        }
        protected void AddGraphPiecesDataItemOne(RectangleF bounds, View3D view3D, GraphDataItem<GraphDataItemBarInfo> item, List<GraphDrawPiece> result)
        {
            this.AddGraphPiecesDataItemOneSide(bounds, view3D, item, Block3DSide.Top, Color.Gray, 1f, -0.25f, result);
            this.AddGraphPiecesDataItemOneSide(bounds, view3D, item, Block3DSide.Bottom, Color.Gray, 1f, -0.45f, result);
            this.AddGraphPiecesDataItemOneSide(bounds, view3D, item, Block3DSide.Left, Color.Gray, 1f, -0.10f, result);
            this.AddGraphPiecesDataItemOneSide(bounds, view3D, item, Block3DSide.Right, Color.Gray, 1f, -0.15f, result);
            this.AddGraphPiecesDataItemOneSide(bounds, view3D, item, Block3DSide.Frontal, Color.DarkGray, 0.05f, 0f, result);
        }
        protected void AddGraphPiecesDataItemOneSide(RectangleF bounds, View3D view3D, GraphDataItem<GraphDataItemBarInfo> item, Block3DSide side, Color lineColor, float lineWidth, float fillColorChange, List<GraphDrawPiece> result)
        {
            Block3D itemBlock = item.GraphItemInfo.CurrentBlock;
            if (itemBlock.IsSideVisibleFromPoint(side, view3D.ObserverPoint))
            {
                GraphDrawPiece piece = new GraphDrawPiece()
                {
                    PieceType = GraphDrawPieceType.Path, Bounds = bounds, FillColor = item.GraphColor.ChangeColor(fillColorChange),
                    LineColor = lineColor, LineWidth = lineWidth,
                    Path = view3D.TranslateToPath(itemBlock.Sides[side])
                };
                result.Add(piece);
            }
        }
        #endregion
        #region Příprava prostorů grafu = rozmisťování objektů a určení jejich velikosti podle typu grafu a číselných hodnot prvků
        private List<GraphDataItem<GraphDataItemBarInfo>> PrepareDataItems(View3D view3D)
        {
            List<GraphDataItem<GraphDataItemBarInfo>> items = new List<GraphDataItem<GraphDataItemBarInfo>>();

            List<GraphDataItem<GraphDataItemBarInfo>> list = this.GetSortedItems(this.Owner.SortType);
            if (list == null) return items;

            int itemCount = list.Count;
            float itemWidth = 0.75f * this.CurrentBlock.Width / (float)itemCount;                         // Šířka prvků celkem = 85% šířky grafu
            float spacing1 = 0.25f * this.CurrentBlock.Width / (float)(itemCount + 1);                    // Mezery mezi prvky = 15% celkové šířky prvku, počet = +1
            float currentX = this.CurrentBlock.X1 + spacing1;
            decimal valueMax = this.ValueMax.Value;
            float y0 = this.CurrentBlock.Y2 - 0.2f * spacing1;
            float yh = this.CurrentBlock.Height - 1.4f * spacing1;
            float dp = this.CurrentBlock.Depth * 0.85f;
            foreach (GraphDataItem<GraphDataItemBarInfo> item in list)
            {
                item.GraphItemInfo = new GraphDataItemBarInfo();
                item.GraphItemInfo.CurrentBlock = CreateItemBlock(currentX, y0, itemWidth, dp, yh, valueMax, item.Value);
                item.GraphItemInfo.ObserverDistance = view3D.FindMinObserverDistance(new Point3D[] { item.GraphItemInfo.CurrentBlock.Points[Block3DVertex.LBF], item.GraphItemInfo.CurrentBlock.Points[Block3DVertex.RBF] }).Value;
                items.Add(item);

                currentX += itemWidth + spacing1;
            }

            items.Sort(GraphDataItemBarInfo.CompareByObserverDistanceDesc);

            return items;
        }
        private Block3D CreateItemBlock(float x, float b, float w, float d, float hMax, decimal valueMax, decimal value)
        {
            float h = ((valueMax == 0m || value == 0m) ? 0f : hMax * (float)(value / valueMax));
            return new Block3D(new Point3D(x, b - h, 0f), new Size3D(w, h, d));
        }
        #endregion
    }
    /// <summary>
    /// GraphDataItemBarInfo : podpůrná data pro jednu každou položku grafu Bar
    /// </summary>
    public class GraphDataItemBarInfo
    {
        public Block3D CurrentBlock { get; set; }
        public float ObserverDistance { get; set; }
        public static int CompareByObserverDistanceDesc(GraphDataItem<GraphDataItemBarInfo> a, GraphDataItem<GraphDataItemBarInfo> b)
        {
            return b.GraphItemInfo.ObserverDistance.CompareTo(a.GraphItemInfo.ObserverDistance);
        }
    }
    #endregion
}
